package k2_reflection.demo01;

import java.lang.reflect.*;
import java.util.ArrayList;
import java.util.Arrays;

public class ClassMethod {

    /**
     * 关于包名 类名 和类相关的方法
     */
    public static <T1,T2> void ref_class(Class<T1> subClazz,Class<T2> superClazz){
        Package pack = subClazz.getPackage();
        System.out.println(pack);

        String packageName = pack.getName();
        System.out.println(packageName);

        //获取类的全路径类名
        String className = subClazz.getName();
        System.out.println(className);

        //只获取类名本身
        String classSimpleName = subClazz.getSimpleName();
        System.out.println(classSimpleName);

        try {
            //@TODO 可以使用Class类型的对象创建实例化对象，需要结合其他方法获取属性和方法等内容后才能结合使用
            //从 JDK9版本开始，由于1.对异常的处理不好 2.只能使用无参的构造方法 所以变为了弃用的方法
            T1 t = subClazz.newInstance();
        } catch (Exception e) {
            throw new RuntimeException(e);
        }

        //获取所有访问修饰符所对应数字之和
        int modifiersNum = String.class.getModifiers();
        System.out.println(modifiersNum);
        //可以把数字之和转成具体的修饰符
        String modifiersString = Modifier.toString(modifiersNum);
        System.out.println(modifiersString);
        //也可以分别看每个修饰符对应的数字是多少
        System.out.println(Modifier.FINAL);
        System.out.println(Modifier.PUBLIC);

        //获取父类
        Class supClazz = subClazz.getSuperclass();
        System.out.println(supClazz.getSimpleName());

        //获取所有的内部类
        Class[] innerClasses = subClazz.getClasses();
        for (Class innerClass : innerClasses) {
            System.out.println(innerClass.getSimpleName());
        }

        //判断某个类是否为另一个类的父类，只要在继承这条线上的类关系都可以
        System.out.println(superClazz.isAssignableFrom(Student.class));
        //而这种形式只能判断直接父类
        Class stuClazz = Student.class;
        System.out.println(stuClazz.getSuperclass() == superClazz);

        Class myListClazz = MyArrayList.class;
        //获取父类的类型后，无法使用泛型；所以用Type接口的子接口接收
        ParameterizedType type = (ParameterizedType) myListClazz.getGenericSuperclass();
        //把所有的泛型变成数组的形式存储，需要用到哪个按照索引就可以获取到
        Type[] types = type.getActualTypeArguments();
        System.out.println(types[0].getClass());
    }

    /**
     * 关于属性的方法
     */
    public static <T> void ref_field(Class<T> subClazz) throws Exception {
        //当前类以及父类所有的public修饰的属性
        Field[] publicFields = subClazz.getFields();
        System.out.println(Arrays.toString(publicFields));

        //当前类中(所有修饰符修饰的)所有属性
        Field[] currentClassFields = subClazz.getDeclaredFields();
        System.out.println(Arrays.toString(currentClassFields));

        //按照指定的属性名封装成Field类型，查找的范围是当前类以及父类所有的public修饰的属性
        Field publicField = subClazz.getField("height");
        System.out.println(publicField.getName());

        //按照指定的属性名封装成Field类型，查找的范围是当前类中(所有修饰符修饰的)所有属性
        Field currentClassField = subClazz.getDeclaredField("name");
        System.out.println(currentClassField.getName());

        //获取修饰符，和属性的类型
        System.out.println(currentClassField.getModifiers());
        System.out.println(Modifier.toString(currentClassField.getModifiers()));
        System.out.println(currentClassField.getType());

        //获取Class类型的实例化对象，如果明确知道类型，可以进行强制转换，如果不明确类型，可以使用泛型接收
        T t = subClazz.newInstance();
        //反射中可以对私有的属性进行暴力的处理，允许访问和设置
        currentClassField.setAccessible(true);
        //可以用Field类型进行获取和设置值，形式相当于被动语态
        currentClassField.set(t,"tom");
        System.out.println(currentClassField.get(t));

    }

    /**
     * 关于构造方法的方法
     */
    public static <T> void ref_constructor(Class<T> subClazz) throws Exception {

        //当前类中所有的public修饰的构造方法
        Constructor[] publicConstructors = subClazz.getConstructors();
        System.out.println(Arrays.toString(publicConstructors));

        //当前类中所有的构造方法
        Constructor[] currentClassConstructors = subClazz.getDeclaredConstructors();
        System.out.println(Arrays.toString(currentClassConstructors));

        ////当前类中所有的public修饰的构造方法中按照参数进行查找
        Constructor publicConstructor = subClazz.getConstructor();
        System.out.println(publicConstructor);

        //当前类中所有的构造方法按照参数进行查找
        Constructor currentClassConstructor = subClazz.getDeclaredConstructor(int.class,String.class);
        System.out.println(currentClassConstructor);

        currentClassConstructor.setAccessible(true);
        Object t = currentClassConstructor.newInstance(1,"tom");

        Field nameField = subClazz.getDeclaredField("name");
        nameField.setAccessible(true);
        System.out.println(nameField.get(t));


    }

    /**
     * 关于普通方法的方法
     */
    public static <T> void ref_method(Class<T> subClazz) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {

        Method[] publicMethods = subClazz.getMethods();
        System.out.println(Arrays.toString(publicMethods));

        Method[] currentClassMethods = subClazz.getDeclaredMethods();
        System.out.println(Arrays.toString(currentClassMethods));

        Method publicMethod = subClazz.getMethod("walk");
        System.out.println(publicMethod);


        Method currentClassMethod = subClazz.getDeclaredMethod("jump",int.class);
        System.out.println(currentClassMethod);

        currentClassMethod.setAccessible(true);
        System.out.println(currentClassMethod.invoke(new Person(), 345));


    }

    public static void main(String[] args) throws Exception {

        Class subClazz = Person.class;
        Class superClazz = Biology.class;


        ref_method(subClazz);


    }

}
